// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:generate go run makeisprint。go-输出iPrint。go 

package strconv

import (
	"unicode/utf8"
)

const (
	lowerhex = "0123456789abcdef"
	upperhex = "0123456789ABCDEF"
)

// 包含字符串是否包含字节c的报告。
func contains(s string, c byte) bool {
	return index(s, c) != -1
}

func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string {
	return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly))
}

func quoteRuneWith(r rune, quote byte, ASCIIonly, graphicOnly bool) string {
	return string(appendQuotedRuneWith(nil, r, quote, ASCIIonly, graphicOnly))
}

func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte {
	// 通常用大字符串调用，因此需要预先分配。如果有引用，
	// 这是保守的，但仍然有很大帮助。
	if cap(buf)-len(buf) < len(s) {
		nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
		copy(nBuf, buf)
		buf = nBuf
	}
	buf = append(buf, quote)
	for width := 0; len(s) > 0; s = s[width:] {
		r := rune(s[0])
		width = 1
		if r >= utf8.RuneSelf {
			r, width = utf8.DecodeRuneInString(s)
		}
		if width == 1 && r == utf8.RuneError {
			buf = append(buf, `\x`...)
			buf = append(buf, lowerhex[s[0]>>4])
			buf = append(buf, lowerhex[s[0]&0xF])
			continue
		}
		buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly)
	}
	buf = append(buf, quote)
	return buf
}

func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
	buf = append(buf, quote)
	if !utf8.ValidRune(r) {
		r = utf8.RuneError
	}
	buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly)
	buf = append(buf, quote)
	return buf
}

func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
	var runeTmp [utf8.UTFMax]byte
	if r == rune(quote) || r == '\\' { // 始终反斜杠
		buf = append(buf, '\\')
		buf = append(buf, byte(r))
		return buf
	}
	if ASCIIonly {
		if r < utf8.RuneSelf && IsPrint(r) {
			buf = append(buf, byte(r))
			return buf
		}
	} else if IsPrint(r) || graphicOnly && isInGraphicList(r) {
		n := utf8.EncodeRune(runeTmp[:], r)
		buf = append(buf, runeTmp[:n]...)
		return buf
	}
	switch r {
	case '\a':
		buf = append(buf, `\a`...)
	case '\b':
		buf = append(buf, `\b`...)
	case '\f':
		buf = append(buf, `\f`...)
	case '\n':
		buf = append(buf, `\n`...)
	case '\r':
		buf = append(buf, `\r`...)
	case '\t':
		buf = append(buf, `\t`...)
	case '\v':
		buf = append(buf, `\v`...)
	default:
		switch {
		case r < ' ':
			buf = append(buf, `\x`...)
			buf = append(buf, lowerhex[byte(r)>>4])
			buf = append(buf, lowerhex[byte(r)&0xF])
		case !utf8.ValidRune(r):
			r = 0xFFFD
			fallthrough
		case r < 0x10000:
			buf = append(buf, `\u`...)
			for s := 12; s >= 0; s -= 4 {
				buf = append(buf, lowerhex[r>>uint(s)&0xF])
			}
		default:
			buf = append(buf, `\U`...)
			for s := 28; s >= 0; s -= 4 {
				buf = append(buf, lowerhex[r>>uint(s)&0xF])
			}
		}
	}
	return buf
}

// Quote返回代表s的双引号Go字符串文字。
// 返回的字符串对
// 控制字符和
// IsPrint定义的不可打印字符使用Go转义序列（\t\n\xFF\u0100）。
func Quote(s string) string {
	return quoteWith(s, '"', false, false)
}

// AppendQuote将一个双引号的Go字符串文本追加到dst，表示由Quote生成的s，
// ，并返回扩展缓冲区。
func AppendQuote(dst []byte, s string) []byte {
	return appendQuotedWith(dst, s, '"', false, false)
}

// QuoteToASCII返回一个双引号的Go字符串文字，表示s。
// 返回的字符串使用Go转义序列（\t\n\xFF\u0100）表示
// 非ASCII字符和iPrint定义的不可打印字符。
func QuoteToASCII(s string) string {
	return quoteWith(s, '"', true, false)
}

// APPENDQUOTETOSCII将QUOTETOSCII生成的代表s的双引号Go字符串文字
// 附加到dst，并返回扩展缓冲区。
func AppendQuoteToASCII(dst []byte, s string) []byte {
	return appendQuotedWith(dst, s, '"', true, false)
}

// QuoteToGraphic返回一个双引号的Go字符串文字，表示s.
// 返回的字符串保留由
// IsGraphic定义的Unicode图形字符，不作任何更改，并对非图形字符使用Go转义序列（\t\n\xFF\u0100）
// 。
func QuoteToGraphic(s string) string {
	return quoteWith(s, '"', false, true)
}

// AppendQuoteGraphic将QuoteGraphic生成的代表s的双引号Go字符串文字
// 附加到dst，并返回扩展缓冲区。
func AppendQuoteToGraphic(dst []byte, s string) []byte {
	return appendQuotedWith(dst, s, '"', false, true)
}

// QuoteRune返回一个带引号的Go字符文本，表示
// rune。返回的字符串使用Go转义序列（\t、\n、\xFF、\u0100）
// 作为IsPrint定义的控制字符和不可打印字符。
func QuoteRune(r rune) string {
	return quoteRuneWith(r, '\'', false, false)
}

// AppendQuoteRune向dst追加一个单引号Go字符，表示QuoteRune生成的符文
// ，并返回扩展缓冲区。
func AppendQuoteRune(dst []byte, r rune) []byte {
	return appendQuotedRuneWith(dst, r, '\'', false, false)
}

// QuoteRunToAscii返回一个带引号的Go字符文本，代表
// /符文。返回的字符串使用Go转义序列（\t、\n、\xFF、
// /\u0100）表示iPrint定义的非ASCII字符和不可打印字符。
func QuoteRuneToASCII(r rune) string {
	return quoteRuneWith(r, '\'', true, false)
}

// APPENDQUOTERONETOASCII将表示由QUOTERONETOASCII生成的符文
// 的单引号Go字符文字追加到dst，并返回扩展缓冲区。
func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
	return appendQuotedRuneWith(dst, r, '\'', true, false)
}

// QuoteRuneToGraphic返回一个带引号的Go字符文本，代表
// 符文。如果符文不是IsGraphic定义的Unicode图形字符
// ，则返回的字符串将使用转义序列
// （\t\n\xFF\u0100）。
func QuoteRuneToGraphic(r rune) string {
	return quoteRuneWith(r, '\'', false, true)
}

// AppendQuoteRuneToGraphic将表示QuoteRuneToGraphic生成的符文的单个带引号的Go字符文本
// 附加到dst，并返回扩展缓冲区。
func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte {
	return appendQuotedRuneWith(dst, r, '\'', false, true)
}

// CanBackquote报告字符串s是否可以表示为
// 不带控制
// 的单行反引号字符串。
func CanBackquote(s string) bool {
	for len(s) > 0 {
		r, wid := utf8.DecodeRuneInString(s)
		s = s[wid:]
		if wid > 1 {
			if r == '\ufeff' {
				return false // BOM是不可见的，不应被引用。
			}
			continue // 所有其他多字节符文都已正确编码并假定可打印。
		}
		if r == utf8.RuneError {
			return false
		}
		if (r < ' ' && r != '\t') || r == '`' || r == '\u007F' {
			return false
		}
	}
	return true
}

func unhex(b byte) (v rune, ok bool) {
	c := rune(b)
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}
	return
}

// UnquoteChar对转义字符串
// 或由字符串s表示的字符文字进行解码。
// 返回四个值：
// 
// 1）值、解码的Unicode码点或字节值；
// 2）多字节，一个布尔值，指示解码字符是否需要多字节UTF-8表示；
// 3）尾，字符后字符串的剩余部分；
// 4）如果字符在语法上有效，则错误为零。
// 
// 第二个参数quote指定要分析的文本类型
// 因此允许使用哪个转义quote字符。
// 如果设置为单引号，则允许序列\，不允许未替换。
// 如果设置为双引号，则允许\“且不允许未替换”。
// 如果设置为零，则不允许任何一个转义，并允许两个引号字符都显示为未转义。
func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
	// 简单的案例
	if len(s) == 0 {
		err = ErrSyntax
		return
	}
	switch c := s[0]; {
	case c == quote && (quote == '\'' || quote == '"'):
		err = ErrSyntax
		return
	case c >= utf8.RuneSelf:
		r, size := utf8.DecodeRuneInString(s)
		return r, true, s[size:], nil
	case c != '\\':
		return rune(s[0]), false, s[1:], nil
	}

	// 硬案例：c是反斜杠
	if len(s) <= 1 {
		err = ErrSyntax
		return
	}
	c := s[1]
	s = s[2:]

	switch c {
	case 'a':
		value = '\a'
	case 'b':
		value = '\b'
	case 'f':
		value = '\f'
	case 'n':
		value = '\n'
	case 'r':
		value = '\r'
	case 't':
		value = '\t'
	case 'v':
		value = '\v'
	case 'x', 'u', 'U':
		n := 0
		switch c {
		case 'x':
			n = 2
		case 'u':
			n = 4
		case 'U':
			n = 8
		}
		var v rune
		if len(s) < n {
			err = ErrSyntax
			return
		}
		for j := 0; j < n; j++ {
			x, ok := unhex(s[j])
			if !ok {
				err = ErrSyntax
				return
			}
			v = v<<4 | x
		}
		s = s[n:]
		if c == 'x' {
			// 单字节字符串，可能不是UTF-8 
			value = v
			break
		}
		if !utf8.ValidRune(v) {
			err = ErrSyntax
			return
		}
		value = v
		multibyte = true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		v := rune(c) - '0'
		if len(s) < 2 {
			err = ErrSyntax
			return
		}
		for j := 0; j < 2; j++ { // 已经有一位；另外两个
			x := rune(s[j]) - '0'
			if x < 0 || x > 7 {
				err = ErrSyntax
				return
			}
			v = (v << 3) | x
		}
		s = s[2:]
		if v > 255 {
			err = ErrSyntax
			return
		}
		value = v
	case '\\':
		value = '\\'
	case '\'', '"':
		if c != quote {
			err = ErrSyntax
			return
		}
		value = rune(c)
	default:
		err = ErrSyntax
		return
	}
	tail = s
	return
}

// QuotedPrefix返回s前缀处的带引号的字符串（如Unquote所理解）。
// 如果s不是以有效的带引号的字符串开头，QuotedPrefix返回错误。
func QuotedPrefix(s string) (string, error) {
	out, _, err := unquote(s, false)
	return out, err
}

// Unquote将s解释为单引号、双引号、
// 或反引号的Go字符串文字，返回s引号中的字符串值
// 。（如果s是单引号，它将是Go 
// 字符文字；Unquote返回相应的
// 一个字符串。）
func Unquote(s string) (string, error) {
	out, rem, err := unquote(s, true)
	if len(rem) > 0 {
		return "", ErrSyntax
	}
	return out, err
}

// unquote在输入的开头解析带引号的字符串，
// 返回解析后的前缀、剩余后缀和任何解析错误。
// 如果unescape为true，则解析的前缀为unescape，否则输入前缀将逐字提供。
func unquote(in string, unescape bool) (out, rem string, err error) {
	// 确定报价表并乐观地找到终止报价。
	if len(in) < 2 {
		return "", in, ErrSyntax
	}
	quote := in[0]
	end := index(in[1:], quote)
	if end < 0 {
		return "", in, ErrSyntax
	}
	end += 2 // 终止报价后的位置；如果存在转义序列

	switch quote {
	case '`':
		switch {
		case !unescape:
			out = in[:end] // 包含引号
		case !contains(in[:end], '\r'):
			out = in[len("`") : end-len("`")] // 排除引号
		default:
			// 原始字符串文本中的回车字符（'\r'）
			// 将从原始字符串值中丢弃。
			buf := make([]byte, 0, end-len("`")-len("\r")-len("`"))
			for i := len("`"); i < end-len("`"); i++ {
				if in[i] != '\r' {
					buf = append(buf, in[i])
				}
			}
			out = string(buf)
		}
		// 注意：之前的实现没有验证原始字符串是否由有效的UTF-8字符组成，我们将继续不进行验证。
		// Go规范没有明确要求有效的UTF-8，
		// 但只提到它对Go源代码
		// （必须是有效的UTF-8）隐式有效。
		return out, in[end:], nil
	case '"', '\'':
		// 处理带引号的字符串，不带任何转义序列。
		if !contains(in[:end], '\\') && !contains(in[:end], '\n') {
			var valid bool
			switch quote {
			case '"':
				valid = utf8.ValidString(in[len(`"`) : end-len(`"`)])
			case '\'':
				r, n := utf8.DecodeRuneInString(in[len("'") : end-len("'")])
				valid = len("'")+n+len("'") == end && (r != utf8.RuneError || n != 1)
			}
			if valid {
				out = in[:end]
				if unescape {
					out = out[1 : end-1] // 排除引号
				}
				return out, in[end:], nil
			}
		}

		// 使用转义序列处理带引号的字符串。
		var buf []byte
		in0 := in
		in = in[1:] // 跳过起始引号
		if unescape {
			buf = make([]byte, 0, 3*end/2) // 尝试避免更多分配
		}
		for len(in) > 0 && in[0] != quote {
			// 处理下一个字符，
			// 拒绝任何无效的未替换换行符。
			r, multibyte, rem, err := UnquoteChar(in, quote)
			if in[0] == '\n' || err != nil {
				return "", in0, ErrSyntax
			}
			in = rem

			// 如果不跳过输入，则追加字符。
			if unescape {
				if r < utf8.RuneSelf || !multibyte {
					buf = append(buf, byte(r))
				} else {
					var arr [utf8.UTFMax]byte
					n := utf8.EncodeRune(arr[:], r)
					buf = append(buf, arr[:n]...)
				}
			}

			// 单引号字符串必须是单字符。
			if quote == '\'' {
				break
			}
		}

		// 验证字符串是否以终止引号结尾。
		if !(len(in) > 0 && in[0] == quote) {
			return "", in0, ErrSyntax
		}
		in = in[1:] // 跳过终止引号

		if unescape {
			return string(buf), in, nil
		}
		return in0[:len(in0)-len(in)], in, nil
	default:
		return "", in, ErrSyntax
	}
}

// bsearch16返回最小的i，使得a[i]>=x。
// 如果没有这样的i，bsearch16返回len（a）。
func bsearch16(a []uint16, x uint16) int {
	i, j := 0, len(a)
	for i < j {
		h := i + (j-i)>>1
		if a[h] < x {
			i = h + 1
		} else {
			j = h
		}
	}
	return i
}

// bsearch32返回最小的i，使得a[i]>=x。如果没有这样的i，bsearch32返回len（a）。
func bsearch32(a []uint32, x uint32) int {
	i, j := 0, len(a)
	for i < j {
		h := i + (j-i)>>1
		if a[h] < x {
			i = h + 1
		} else {
			j = h
		}
	}
	return i
}

// TODO:iPrint是unicode的本地实现。i打印，经
// /测试验证，给出相同答案。它允许这个包不依赖unicode，
// 因此不需要拉入所有unicode表。如果链接器更好地处理未使用的表，我们就可以摆脱这个实现。那太好了。

// IsPrint报告符文是否定义为可由Go打印，
// 定义与unicode相同。iPrint：字母、数字、标点符号、
// 符号和ASCII空格。
func IsPrint(r rune) bool {
	// 快速检查拉丁语-1 
	if r <= 0xFF {
		if 0x20 <= r && r <= 0x7E {
			// 所有ASCII码都可以从空间通过DEL-1打印。
			return true
		}
		if 0xA1 <= r && r <= 0xFF {
			// 类似地，用于“通过”。。。
			return r != 0xAD // /。。。除了奇怪的软连字符。
		}
		return false
	}

	// 在uint16或uint32值上使用相同的算法。
	// 首先，先找到i，使isPrint[i]>=x。
	// 这是可能跨越x的一对的开始或结束的索引。
	// 开始是偶数（isPrint[i&^1]），结束是奇数（isPrint[i|1]）。
	// 如果我们在某个范围内找到x，请确保x不在isNotPrint列表中。

	if 0 <= r && r < 1<<16 {
		rr, isPrint, isNotPrint := uint16(r), isPrint16, isNotPrint16
		i := bsearch16(isPrint, rr)
		if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
			return false
		}
		j := bsearch16(isNotPrint, rr)
		return j >= len(isNotPrint) || isNotPrint[j] != rr
	}

	rr, isPrint, isNotPrint := uint32(r), isPrint32, isNotPrint32
	i := bsearch32(isPrint, rr)
	if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
		return false
	}
	if r >= 0x20000 {
		return true
	}
	r -= 0x10000
	j := bsearch16(isNotPrint, uint16(r))
	return j >= len(isNotPrint) || isNotPrint[j] != uint16(r)
}

// IsGraphic报告符文是否由Unicode定义为图形。此类字符包括字母、标记、数字、标点符号、符号，以及L、M、N、P、S和Zs类别中的空格。
func IsGraphic(r rune) bool {
	if IsPrint(r) {
		return true
	}
	return isInGraphicList(r)
}

// IsingGraphicList报告符文是否在isGraphic列表中。将
// 与IsGraphic分开，可以让quoteWith避免两次调用iPrint。
// 仅当iPrint失败时才应调用。
func isInGraphicList(r rune) bool {
	// 我们知道r必须适合16位-请参阅MakeiPrint。去
	if r > 0xFFFF {
		return false
	}
	rr := uint16(r)
	i := bsearch16(isGraphic, rr)
	return i < len(isGraphic) && rr == isGraphic[i]
}
